////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//               Random Quantized Frame Generation Routines                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//Header File
#include "quantized_frame_generator.h"
//Referenced Libraries
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <dirent.h>
#include <time.h>

///////////////////////////////  generate_frame  ///////////////////////////////
//                                                                            //
//      >> Routine that generate a random quantized frame											//
//                                                                            //
//      -- int number_of_macroblocks	:	total number of Macroblocks generated //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
int generate_frame()
{
	int number_of_macroblocks = 0;
	int frame[VERTICAL_MACROBLOCKS][HORIZONTAL_MACROBLOCKS][NUMBER_OF_BLOCKS][NUMBER_OF_COEFFICIENTS];
	int **macroblock;
	int i,j;

	srand(time(NULL));

	for( i = 0; i < VERTICAL_MACROBLOCKS; i++ )
		for( j = 0; j < HORIZONTAL_MACROBLOCKS; j++ ){
			macroblock = generate_macroblock();
			copy_macroblock( frame[i][j], macroblock );
			free( macroblock );
			number_of_macroblocks++;
/*
			//save macroblock to a file
			write_macroblock(i,j);
			if(flagShowGenMacroblocks)
			printf("\n\n\tMacroblock[%d][%d] -- Generated!!!",i,j);
			*/
		}
/*
	  if(flagShowGenMacroblocks){
			printf("\n\n\tMacroblock quantity: : %d.",number_of_macroblocks);
			getchar();
			printf("\n\n");
		}
*/
	//write_frame();
}

///////////////////////////    genRandomMacroblock  ////////////////////////////
//                                                                            //
//      >> Routine that generate a random macroblock                          //
//                                                                            //
//      -- int genBlock :   the random blocks generated to the Macroblock     //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
int **generate_macroblock()
{
	int *generated_block;
	int **macroblock;
	int i;
	
	macroblock = malloc (sizeof(int)*NUMBER_OF_BLOCKS);
	for( i = 0; i < NUMBER_OF_BLOCKS; i++ ){
		generated_block = generate_block();
		macroblock[i] = generated_block;
	}
}

/////////////////////////////    generate_block  ///////////////////////////////
//                                                                            //
//      >> Routine that generated a random block. It generated random         //
//         numbers for total of coefficients (total_coeffs), total of         //
//         Trailing Ones (total_T1), for total of zeros preceding the         //
//         last coefficient in the mapped string (total_zeros) and for        //
//         string of Levels (string_levels), string of Trailing Ones 		      //
//         (string_T1) and string of Run Befores (string_run_before)          //
//                                                                            //
//      -- char *string  					:   pointer to the string mounted from the	//
//																	  random generated values                 //
//      -- int random_number     	:   a random generated number               //
//      -- int total_coeffs      	:   total of Coefficients                   //
//      -- int total_T1          	:   total of Trailing Ones                  //
//      -- int total_levels      	:   total of Levels                     		//
//      -- int total_zeros       	:   total of zeros preceding the last       //
//	                                  coefficients in the mapped string       //
//      -- int *string_coeffs    	:   pointer to the array of Coefficients    //
//      -- int *string_T1        	:   pointer to the array of Trailing Ones   //
//      -- int *string_run_before :   pointer to the array of Run Befores of  //
// 		                                each coefficient                        //
//      -- int zeros_left        	:   the number of zeros that have not yet   //
//                                  	been encoded                            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
char **generate_block()
{
	int i;
	int random_number;
	int total_coeffs, total_T1, total_levels, total_zeros;
	int zeros_left;
	int aux = 0, aux1;
	int *string_levels, *string_T1, *string_run_before;
	char *string;
	
	srand(time(NULL));
	
	//generate a random number of coefficients
	random_number = (rand() % ( (NUMBER_OF_COEFFICIENTS/2) + 1 ));
	while(random_number == 0)
	  random_number = (rand() % ( (NUMBER_OF_COEFFICIENTS/2) + 1 ));
	total_coeffs = random_number;

	//generate a random number of total of zeros before the last coefficient
	total_zeros = (rand() % ( ((NUMBER_OF_COEFFICIENTS/2) - (total_coeffs/2)) + 1 ));

	//initialize zeros_left
	zeros_left = total_zeros;
    
	//generate a random number of trailing ones
	if(total_coeffs < 3)
	  total_T1 = (rand() % ( total_coeffs + 1 ));
	else
	  total_T1 = (rand() % ( 3 + 1 ));
	
	//calculate the number of levels from the number of coefficients
	total_levels = total_coeffs - total_T1;
	
	//alocate memory for levels string, trailing ones string and run before string
	string_levels = (int*) malloc((total_levels) * sizeof(int));
	string_T1 = (int*) malloc(total_T1 * sizeof(int));
	string_run_before = (int*) malloc(total_coeffs * sizeof(int));

	//generate random levels
	for(i = 0; i < total_levels; i++){
		random_number = BOTTOM_LIMIT + (rand() % ((UP_LIMIT*2) + 1));
		while(random_number == 0)
			random_number = BOTTOM_LIMIT + (rand() % ((UP_LIMIT*2) + 1));
		string_levels[i] = random_number;
  }

	//put the level with major absolute value in the end of string
	if(total_levels > 0){
		for(i = 0; i < total_levels; i++)
			if(abs(string_levels[i]) > aux){
				aux1 = i;
				aux = abs(string_levels[i]);
	    }
	  string_levels[aux1] = string_levels[total_levels - 1];
	  string_levels[total_levels - 1] = aux;
  }

	//generate random trailing ones
	for(i = 0; i < total_T1; i++){
		if(rand() % 2 == 0)
			string_T1[i] = 1;
		else
			string_T1[i] = -1;
	}
	
	//generate random run befores for each coefficient
	for(i = 0; i < total_coeffs; i++){
	  string_run_before[i] = (rand() % (zeros_left+1));
	  zeros_left = zeros_left - string_run_before[i];
	}
	if(zeros_left > 0)
		string_run_before[i-1] = zeros_left;
	
	//show DEBUG messages
	#ifdef DEBUG
		printf("Total Coeffs:\t%d\n", total_coeffs);
		printf("Total T1:\t%d\n", total_T1);
		printf("Total Levels:\t%d\n", total_levels);
		printf("Total Zeros:\t%d\n", total_zeros);
		printf("Levels:\t\t");
		for(i = 0; i < total_levels; i++)
			printf("%d\t", string_levels[i]);
		printf("\n");
		printf("T1:\t\t");
		for(i = 0; i < total_T1; i++)
			printf("%d\t", string_T1[i]);
		printf("\n");
		printf("RB:\t\t");
		for(i = 0; i < total_coeffs; i++)
			printf("%d\t", string_run_before[i]);
		printf("\n");
	#endif
	
	//remount a string with all this data
	string = remount_string(total_coeffs, total_T1, total_levels, total_zeros, string_T1, string_levels, string_run_before);
	
	//show DEBUG messages
	#ifdef DEBUG
		printf("String:\t\t");
		for(i = 0; i < NUMBER_OF_COEFFICIENTS; i++)
			printf("%d\t", string[i]);
		printf("\n");
	#endif
	
	//return the generated block
	return inverse_zig_zag_scan(string);
}

/////////////////////////////    remount_string  ///////////////////////////////
//                                                                            //
//      >> Routine that mount a string from the follow data:                  //
//                                                                            //
//      -- int total_coeffs				:   total of nonzero coefficients						//
//      -- int total_T1						:   total of Trailing Ones               		//
//      -- int total_levels				:   total of Levels													//
//      -- int total_zeros				:   total of zeros preceding last nonzero		//
//                               			coefficient                           	//
//      -- int *string_T1					:   pointer to the string of Trailing Ones	//
//      -- int *string_levels			:   pointer to the string of Levels         //
//      -- int *stringRunBefore		:   pointer to the string of Run Before of	//
//																		each nonzero coefficient                //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
char *remount_string(int total_coeffs, int total_T1, int total_levels, int total_zeros, int *string_T1, int *string_levels, int *string_run_before)
{
	int i,j,k;
	char *string = (char *) malloc(sizeof(char) * (NUMBER_OF_COEFFICIENTS + 1));
	
	//initialize string
	for(i = 0; i < NUMBER_OF_COEFFICIENTS; i++)
		string[i] = 0;
	string[NUMBER_OF_COEFFICIENTS] = '\0';

	//insert the trailing ones into the string and them run before
	for(i = 0, j = (total_coeffs + total_zeros) - 1; i < total_T1; j -= (string_run_before[i] + 1), i++)
		string[j] = string_T1[i];
	
	//insert the levels into the string and them run before
	for(k = 0; k < total_levels; j -= (string_run_before[i] + 1), i++, k++)
		string[j] = string_levels[k];
	
	return string;
}

///////////////////////////    inverse_zig_zag_scan  ///////////////////////////
//                                                                            //
//      >> Routine that map a 16-element string to a block in zig-zag order 	//
//                                                                            //
//      -- char *string		:   pointer to the 16-element string				        //
//      -- char *block		:   pointer to the mapped block                     //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
char **inverse_zig_zag_scan(char *string)
{
	int i;
	char **block = (char **) malloc (sizeof(char) * (NUMBER_OF_COEFFICIENTS/4));
	
	//alocate memory for each line
	for(i = 0; i < NUMBER_OF_COEFFICIENTS/4; i++){
		block[i] = (char *) malloc (sizeof(char) * (NUMBER_OF_COEFFICIENTS/4));
	}
	
	//make the inverse zig-zag scan, from string to block
	block[0][0] = string[0];
	block[0][1] = string[1];
	block[1][0] = string[2];
	block[2][0] = string[3];
	block[1][1] = string[4];
	block[0][2] = string[5];
	block[0][3] = string[6];
	block[1][2] = string[7];
	block[2][1] = string[8];
	block[3][0] = string[9];
	block[3][1] = string[10];
	block[2][2] = string[11];
	block[1][3] = string[12];
	block[2][3] = string[13];
	block[3][2] = string[14];
	block[3][3] = string[15];
	
	return block;
}

////////////////////////////    writeMacroblock  ///////////////////////////////
//                                                                            //
//      >> Routine that save a Macroblock to a text formatted file            //
//                                                                            //
//      -- FILE *outpuFile  :   pointer to the output filename                //
//      -- DIR *dir         :   current directory                             //
//      -- char *name       :   filename of current Macroblock                //
//      -- int i,j          :   the index of the current Macroblock           //
//      -- char index[4]    :   used to name the filename with the index      //
//                              of the Macroblock                             //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
void write_macroblock(int i,int j)
{
	FILE *outputFile;
	DIR *dir;
	char *name;
	char index[4];
	int k,l,m,n;

	dir=opendir("InputGeneratedMacroblocks");

	if(dir==NULL)
	  //creates directory, if necessary
	  system("md InputGeneratedMacroblocks");
	else
	  closedir(dir);

	name=(char*)malloc(sizeof(".//InputGeneratedMacroblocks//GeneratedMacroblock[ ][ ].txt")+1);

	strcpy(name,".////InputGeneratedMacroblocks//GeneratedMacroblock[");
	sprintf(index,"%d",i);
	strcat(name,index);
	strcat(name,"]["); 
	sprintf(index,"%d",j);
	strcat(name,index);
	strcat(name,"].txt");

	if((outputFile=fopen(name,"w"))==NULL){
	   printf("\n\n\tOpening the file has failed.");
	   exit(1);
	}

	fprintf(outputFile,"\n\t\t\t\t\t\t\t\t>>> Macroblock [%d][%d] <<<\n\n",i,j);
	fprintf(outputFile,"\t--------------------------------------------------");
	fprintf(outputFile,"--------------------------------------------------");
	fprintf(outputFile,"-------------------------------------\n");   
	for(k=0;k<4;k++)
	  for(l=0;l<4;l++){
	    fprintf(outputFile,"\t|");
	    for(m=0;m<4;m++)
	      for(n=0;n<4;n++)
	        fprintf(outputFile,"\t%d",frame[i][j][k][m][l][n]);
	    fprintf(outputFile,"\t|\n");
		}
	fprintf(outputFile,"\t--------------------------------------------------");
	fprintf(outputFile,"--------------------------------------------------");
	fprintf(outputFile,"-------------------------------------");
	fclose(outputFile);    
}

//////////////////////////////    writeFrame  //////////////////////////////////
//                                                                            //
//      >> Routine that save the frame to a text formatted file and binary    //
//         file                                                               //
//                                                                            //
//      -- FILE *outputFile :   pointer to the output file, named             // 
//                              "frame.txt" and "frame.bin"                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
int write_frame()
{
	FILE *outputFile;
	int i,j,k,l,m,n;

	if((outputFile=fopen("frame.txt","w"))==NULL){
	  printf("\n\n\tOpening the file has failed.");
	  exit(1);
	}

	for(i=0; i<QCIFVerticalMB; i++)
	  for(k=0;k<4;k++)
	    for(l=0;l<4;l++){
	      for(j=0; j<QCIFHorizontalMB; j++)
	        for(m=0;m<4;m++)
	          for(n=0;n<4;n++)
	            fprintf(outputFile,"%d,\t",frame[i][j][k][m][l][n]);
	      fprintf(outputFile,"\n");
			}
	fclose(outputFile);

	if((outputFile=fopen("frame.bin","wb"))==NULL){
		printf("\n\n\tOpening the file has failed.");
		exit(1);
	}

	for(i=0; i<QCIFVerticalMB; i++)
	  for(j=0; j<QCIFHorizontalMB; j++)
	    for(k=0;k<4;k++)
	      for(l=0;l<4;l++)
	        for(m=0;m<4;m++)
	          for(n=0;n<4;n++)
	            fwrite(&frame[i][j][k][l][m][n],sizeof(int),1,outputFile);
	fclose(outputFile);
}

///////////////////////////////    copyBlock  //////////////////////////////////
//                                                                            //
//      >> Routine that copy a random generated block to the macroblock       //
//         variable                                                           //
//                                                                            //
//      -- int origin[4][4] :   the random generated block                    //
//      -- int i,j          :   the index of the current block, in the        //
//                              Macroblock                                    //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
void copy_block(int origin[4][4],int i,int j)
{
	int k,l;
	for(k=0;k<4;k++)
		for(l=0;l<4;l++)
			macroblock[i][j][k][l]=origin[k][l];
}

////////////////////////////    copyMacroblock  ////////////////////////////////
//                                                                            //
//      >> Routine that copy a random generated Macroblock to the frame       //
//         variable                                                           //
//                                                                            //
//      -- int i,j          :   the index of the current Macroblock, in the   //
//                              frame                                         //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
void copy_macroblock(int i,int j)
{
	int k,l,m,n;
	
	for(k=0;k<4;k++)
		for(l=0;l<4;l++)
			for(m=0;m<4;m++)
				for(n=0;n<4;n++)
					frame[i][j][k][l][m][n]=macroblock[k][l][m][n];
}